package com.spring.theory.mvc.basic.returnResolver;

import cn.hutool.core.collection.CollUtil;

import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler;

/**
 * TestReturnResolver
 *
 * @author H
 * @version 1.0
 * @date 2024-05-10 14:01
 */
@Slf4j
public class TestReturnResolver {

    public static void main(String[] args) throws Exception {
        Method method = ReturnController.class.getMethod("test7");
        ReturnController returnController = new ReturnController();
        Object returnValue = method.invoke(returnController);
        HandlerMethodReturnValueHandlerComposite composite = returnValueResolverComposite();
        HandlerMethod handlerMethod = new HandlerMethod(returnController, method);

        ModelAndViewContainer container = new ModelAndViewContainer();
        MockHttpServletResponse response = new MockHttpServletResponse();
        if (composite.supportsReturnType(handlerMethod.getReturnType())) {
            composite.handleReturnValue(returnValue, handlerMethod.getReturnType(), container, new ServletWebRequest(new MockHttpServletRequest(), response));
        }

        // 视图渲染的内容
        log.info("container:{}", container.getModel());

        // 不走视图渲染流程；获取响应内容
        log.info(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));

    }

    static class ReturnController {

        /**
         * 常用于返回html视图；搭配ModelMap使用
         * 使用 ModelMap 对象来添加数据，这些数据之后可以在返回的视图中被访问
         */
        public ModelAndView test1(ModelMap map) {
            log.info("test1()");
            ModelAndView view = new ModelAndView("view1");
            view.addObject("name", "aaa-test1");
            return view;
        }

        /**
         * 单独返回字符串，会当作视图名字处理
         */
        public String test2() {
            log.info("test2()");
            return "view2";
        }

        /**
         * 返回对象时，会被当成模型数据添加到 modelAndViewContainer 中，后面视图渲染时用
         * 如果没有定义视图名，会把请求路径当作视图名处理
         */
        @ModelAttribute
        @RequestMapping("/test3")
        public ReturnUser test3() {
            log.info("test3()");
            return new ReturnUser("李四", 20);
        }

        public ReturnUser test4() {
            log.info("test4()");
            return new ReturnUser("王五", 30);
        }

        /**
         * 以上请求都需要视图渲染，返回值都会被添加到 modelAndViewContainer 中
         * 以下请求都直接当作 响应体处理 ，不需要视图渲染，生成响应；
         * -都做了操作设置响应处理完成 mavContainer.setRequestHandled(true); 不会走视图解析、视图渲染流程
         */

        //HttpEntity是HTTP 请求或响应实体的一个类；允许封装请求体（body）以及 HTTP 头部信息（headers），使得发送 HTTP 请求时能够同时携带数据和元数据
        public HttpEntity<ReturnUser> test5() {
            log.info("test5()");
            return new HttpEntity<>(new ReturnUser("赵六", 40));
        }

        public HttpHeaders test6() {
            log.info("test6()");
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Type", "text/html");
            return headers;
        }

        @ResponseBody
        public ReturnUser test7() {
            log.info("test7()");
            return new ReturnUser("钱七", 50);
        }

    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public static class ReturnUser {
        private String name;
        private Integer age;
    }

    private static HandlerMethodReturnValueHandlerComposite returnValueResolverComposite() {
        HandlerMethodReturnValueHandlerComposite composite = new HandlerMethodReturnValueHandlerComposite();
        composite.addHandlers(
                CollUtil.toList(
                        // ModelAndView 解析
                        new ModelAndViewMethodReturnValueHandler(),
                        new ViewMethodReturnValueHandler(),
                        // @ModelAttribute 返回值解析
                        new ServletModelAttributeMethodProcessor(false),

                        // HttpEntity 解析 ；传入 转换器 生成对应的json数据
                        new HttpEntityMethodProcessor(CollUtil.toList(new MappingJackson2HttpMessageConverter())),
                        // @ResponseBody 解析
                        new RequestResponseBodyMethodProcessor(CollUtil.toList(new MappingJackson2HttpMessageConverter())),
                        // HttpHeaders 解析
                        new HttpHeadersReturnValueHandler(),
                        new ServletModelAttributeMethodProcessor(true)
                )
        );
        return composite;
    }

}
